package bm.com.framework.security.filter;

import bm.com.framework.common.config.EnumRoute;
import bm.com.framework.common.exception.BmException;
import bm.com.framework.common.unit.DateUnit;
import bm.com.framework.common.unit.Tool;
import bm.com.framework.redis.config.RedisTemplateUnit;
import bm.com.framework.redis.keys.RedisKey;
import bm.com.framework.security.config.SecurityProperties;
import bm.com.framework.web.core.dto.BmSysUserTokenDTO;
import jakarta.annotation.Resource;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;

/**
 * Token 过滤器，验证 token 的有效性
 *
 * @author 芋道源码
 */
@RequiredArgsConstructor
@Slf4j
@Component
public class TokenAuthenticationFilter extends OncePerRequestFilter {

    private final SecurityProperties securityProperties;

    @Resource
    RedisTemplateUnit redisTemplateUnit;

    @Override
    @SuppressWarnings("NullableProblems")
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {

        // 所有的公共，跳过免认证路径
        if (request.getRequestURI().startsWith("/bm"+EnumRoute.CommonRoute.Base)) {
            chain.doFilter(request, response);
            return;
        }

        // 1. 从请求头获取Token
        final String authorization = request.getHeader(securityProperties.getTokenHeader());

        BmSysUserTokenDTO user =  null;

        // 2. 验证Token格式: Bearer <token>
        if (authorization != null) {

            String tokenKey = RedisKey.sysUserToken(authorization);
            String userToken = (String) redisTemplateUnit.redisTemplate().opsForValue().get(tokenKey);
            if (userToken == null) {
                throw new BmException("token已过期,请重新登录");
            }

            user = Tool.fromJson(userToken, BmSysUserTokenDTO.class);
            if (user.getOrgExpireDate() < DateUnit.getNowTimeSecond()){
                throw new BmException("该组织有效期已过，请充值再使用");
            }
                // 4. 创建认证对象并设置到SecurityContext
                UsernamePasswordAuthenticationToken authentication =
                        new UsernamePasswordAuthenticationToken(
                                user,
                                null,
                                null
                        );
                authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(authentication);

        } else {
            logger.warn("Token格式错误");
            throw new BmException("Token格式错误");
        }

        chain.doFilter(request, response);
    }
}
